<?
class errorHandler{
	//Configuration Settings.
	public $config;

	public $errors=array();
	public $fatalErrors=array();
	
	public $isError=false;
	
	public $isFatalError=false;
	
	//Application Data Logger.
	public $logger;

	#Error Levels
	const SEVERITY_OK="severity-ok";
	const SEVERITY_LOW="severity-low";
	const SEVERITY_MEDIUM="severity-medium";
	const SEVERITY_HIGH="severity-high";
	const SEVERITY_EXTREME="severity-extreme";

	#Error Codes - Generic (0,2000->5000)
	public $ERROR_UNSPECIFIED=0;
	
	#  +HTTP Errors (100->1000)
	public $ERROR_FILE_NOT_FOUND=404;
	
	#Error Codes - CORE (5001->10000)
	public $ERROR_CODE_NOT_FOUND=5001;

	public $ERROR_LOG_FILE_NOT_FOUND=5200;

	public $HEADERS_ALREADY_SENT=5300;
	
	#  +Theme Errors (5501->5050)
	public $ERROR_DEFAULT_THEME_NOT_FOUND=5501;
	public $ERROR_THEME_NOT_FOUND=5502;
	
	#  +View Errors (5051->5100)
	public $ERROR_DEFAULT_VIEW_NOT_FOUND=5051;
	public $ERROR_VIEW_NOT_FOUND=5052;
	
	#  +Controller Errors (5101->5150)
	public $ERROR_DEFAULT_CONTROLLER_NOT_FOUND=5101;
	public $ERROR_CONTROLLER_NOT_FOUND=5102;
	
	#  +Database Errors (5151->5200)
	public $ERROR_DB_LINK=5151;
	public $ERROR_DB_SELECT=5152;
	public $ERROR_DB_CONNECTION=5153;
	public $ERROR_DB_QUERY=5154;
	public $ERROR_DB_NO_ROWS_AFFECTED=5155;
	
	#  +Base Model Errors (5201->5300)
	public $ERROR_MODEL_UPDATE=5201;
	public $ERROR_MODEL_INSERT=5202;

	#  +Caching Errors (5301->5400)
	public $ERROR_CACHE_READ=5301;
	public $ERROR_CACHE_SAVE=5301;

	#Error Details Array
	public $ERROR_TYPES=array();
	
	public function __construct(){
		// call the construct
		$this->init();
	}
	
	public function init(){
		
		#Load the config file.
		$this->loadConfig(ENV_MODE);
		
		#Create Data Logger
		$this->logger=new logger();
		
		$this->buildErrorTypes();
		
		#Array of debugs
		$this->debugs=array();
	}
	
	/*
	** defines which config file to use
	**
	**  @return void
	*/
	public function loadConfig($mode=""){      
		$class=$mode;
		$this->config=new $class;
		
		if($this->config->debug===TRUE){
			$this->config->debug="ON";
		}
	}
	
	public function buildErrorTypes(){
		$this->ERROR_TYPES[$this->ERROR_UNSPECIFIED]=array(
			"code"=>"%CODE%",
			"title"=>"An unspecified error occured",
			"description"=>"An unspecified error occured.",
			"fatal"=>false
		);

		$this->ERROR_TYPES[$this->ERROR_FILE_NOT_FOUND]=array(
			"code"=>"%CODE%",
			"title"=>"File Not Found",
			"description"=>"The requested file was not found.",
			"fatal"=>true
		);

		$this->ERROR_TYPES[$this->ERROR_CODE_NOT_FOUND]=array(
			"code"=>"%CODE%",
			"title"=>"Error Code Not Found",
			"description"=>"The error supplied was not found.",
			"fatal"=>true
		);

		$this->ERROR_TYPES[$this->ERROR_LOG_FILE_NOT_FOUND]=array(
			"code"=>"%CODE%",
			"title"=>"Log File Not Found",
			"description"=>"Gravy's application log file was not found.",
			"fatal"=>true
		);

		$this->ERROR_TYPES[$this->HEADERS_ALREADY_SENT]=array(
			"code"=>"%CODE%",
			"title"=>"Data Already Sent To Browser",
			"description"=>"HTTP Headers were already sent to the browser.",
			"fatal"=>true
		);

		$this->ERROR_TYPES[$this->ERROR_DEFAULT_THEME_NOT_FOUND]=$this->ERROR_TYPES[$this->ERROR_FILE_NOT_FOUND];
		$this->ERROR_TYPES[$this->ERROR_THEME_NOT_FOUND]=$this->ERROR_TYPES[$this->ERROR_FILE_NOT_FOUND];

		$this->ERROR_TYPES[$this->ERROR_DEFAULT_VIEW_NOT_FOUND]=$this->ERROR_TYPES[$this->ERROR_FILE_NOT_FOUND];
		$this->ERROR_TYPES[$this->ERROR_VIEW_NOT_FOUND]=$this->ERROR_TYPES[$this->ERROR_FILE_NOT_FOUND];

		$this->ERROR_TYPES[$this->ERROR_DEFAULT_CONTROLLER_NOT_FOUND]=$this->ERROR_TYPES[$this->ERROR_FILE_NOT_FOUND];
		$this->ERROR_TYPES[$this->ERROR_CONTROLLER_NOT_FOUND]=$this->ERROR_TYPES[$this->ERROR_FILE_NOT_FOUND];

		$this->ERROR_TYPES[$this->ERROR_DB_LINK]=array(
			"code"=>"%CODE%",
			"title"=>"Database Error",
			"description"=>"The application could not connect to the database.",
			"fatal"=>false
		);
		$this->ERROR_TYPES[$this->ERROR_DB_SELECT]=$this->ERROR_TYPES[$this->ERROR_DB_LINK];
		$this->ERROR_TYPES[$this->ERROR_DB_CONNECTION]=array(
			"code"=>"%CODE%",
			"title"=>"Database Error",
			"description"=>"The database connection is closed.",
			"fatal"=>false
		);
		$this->ERROR_TYPES[$this->ERROR_DB_QUERY]=array(
			"code"=>"%CODE%",
			"title"=>"Database Error",
			"description"=>"There was an error in the supplied SQL query.",
			"fatal"=>false
		);
		$this->ERROR_TYPES[$this->ERROR_DB_NO_ROWS_AFFECTED]=array(
			"code"=>"%CODE%",
			"title"=>"Database Error",
			"description"=>"No data was updated or selected from the database.",
			"fatal"=>false
		);
		
		$this->ERROR_TYPES[$this->ERROR_MODEL_UPDATE]=array(
			"code"=>"%CODE%",
			"title"=>"Database Error",
			"description"=>"A error occured while trying to update the database.",
			"fatal"=>false
		);
		$this->ERROR_TYPES[$this->ERROR_MODEL_INSERT]=array(
			"code"=>"%CODE%",
			"title"=>"Database Error",
			"description"=>"A error occured while trying to insert data into the database.",
			"fatal"=>false
		);
		$this->ERROR_TYPES[$this->ERROR_CACHE_READ]=array(
			"code"=>"%CODE%",
			"title"=>"Caching Error",
			"description"=>"An error occured trying to read the contents of a cached file.",
			"fatal"=>false
		);
		$this->ERROR_TYPES[$this->ERROR_CACHE_SAVE]=array(
			"code"=>"%CODE%",
			"title"=>"Caching Error",
			"description"=>"An error occured trying to create/save a cached file.",
			"fatal"=>false
		);
	}

	/*
	** Add a Custom Error Function.
	**  This function takes variables that describe an error and adds them to
	**  the errors collection for later debugging use, and also logs the error in the application log.
	**  @return void
	*/
	public function addCustomError($code="",$title="",$description="",$arrayOfAdditionalData=NULL,$fatal=NULL){
		$this->addError($code,$title,$description,$arrayOfAdditionalData,$fatal);
	}

	/*
	** Add Error Function.
	**  This function takes variables that describe an error and adds them to
	**  the errors collection for later debugging use, and also logs the error in the application log.
	**  @return void
	*/
	public function addError($code=NULL,$location="",$arrayOfAdditionalData=NULL,$fatal=NULL,$extra=NULL){
		$this->isError=true;
		$supplied_code=$code;
		if(!@$this->ERROR_TYPES[$code]){
			$code=$this->ERROR_CODE_NOT_FOUND;
		}
		$stacktrace=$this->stacktrace();
		$error=array(
			"code"=>str_replace("%CODE%",$code,$supplied_code),
			"title"=>str_replace("%CODE%",$code,$this->ERROR_TYPES[$code]["title"]),
			"description"=>str_replace("%CODE%",$code,$this->ERROR_TYPES[$code]["description"]),
			"fatal"=>$this->ERROR_TYPES[$code]["fatal"],
			"location"=>str_replace("%CODE%",$code,$location),
			"stacktrace"=>$stacktrace
		);
		if(is_array($supplied_code)){
			$error=$supplied_code;
			$fatal=$arrayOfAdditionalData;
			$arrayOfAdditionalData=$location;
			$code=$supplied_code["code"];
		}elseif(is_string($supplied_code)){
			$error["code"]=$supplied_code;
			$error["title"]=$location;
			$error["description"]=$arrayOfAdditionalData;
			$arrayOfAdditionalData=$fatal;
			$fatal=$extra;
		}
		if(is_array($arrayOfAdditionalData)){
			foreach($arrayOfAdditionalData as $key=>$value){
					$error[$key]=$value;
			}
		}
		if($error["fatal"]){
			$this->isFatalError=true;
		}
		if($fatal!==NULL){
			$error["fatal"]=$fatal;
			$this->isFatalError=$fatal;
		}else{
			$error["fatal"]=$fatal;
			$this->isFatalError=$fatal;
		}
		if($this->isFatalError){
			$this->fatalErrors[]=$error;
		}
		$this->errors[]=$error;
		$this->debug("gravy Error",$error);
		$this->log("ADDED ERROR(addError)\nCode:".$code."\nTitle:".$error["title"]."\nDescription:".$error["description"]."\nLocation:".$error["location"]."\nFatal:".$error["fatal"]."\nStack Trace:{\n".$stacktrace."\n}\nAdditional Data:\n".var_export($arrayOfAdditionalData,true));
	}

	/*
	** Get a StackTrace.
	**  This function returns a textual stacktrace.
	**  @return string
	*/
	public function stacktrace($html=false){
		$output = "";
		$backtrace = debug_backtrace();

		foreach ($backtrace as $bt) {
			$args = '';
			foreach ($bt['args'] as $a) {
				if (!empty($args)) {
					$args .= ', ';
				}
				switch (gettype($a)) {
					case 'integer':
					case 'double':
						$args .= $a;
						break;
					case 'string':
						$a = htmlspecialchars(substr($a, 0, 64)).((strlen($a) > 64) ? '...' : '');
						$args .= "\"$a\"";
						break;
					case 'array':
						$args .= 'Array('.count($a).')';
						break;
					case 'object':
						$args .= 'Object('.get_class($a).')';
						break;
					case 'resource':
						$args .= 'Resource('.strstr($a, '#').')';
						break;
					case 'boolean':
						$args .= $a ? 'True' : 'False';
						break;
					case 'NULL':
						$args .= 'Null';
						break;
					default:
						$args .= 'Unknown';
				}
			}
			$output .= "\n";
			$output .= "File: Line {$bt['line']} in {$bt['file']}\n";
			$output .= "Call: {$bt['class']}{$bt['type']}{$bt['function']}($args)\n";
		}
		$output .= "\n";
		return($output);
	}


	/*
	** Get the First Error.
	**  This function gets the first error, then returns it as an associative array.
	**  @return array
	*/
	public function getFirstError($fatal=false,$ifNotFatalGetNormalError=true){
		$result=false;
		if($this->isError){
			if($fatal&&$this->isFatalError){
				$result=$this->fatalErrors[0];
			}
			if(!$result&&(($fatal&&$ifNotFatalGetNormalError)||!$fatal)){
				$result=$this->errors[0];
			}
		}
		return($result);
	}
	
	/*
	** Get the Last Error.
	**  This function gets the last error, then returns it as an associative array.
	**  @return array
	*/
	public function getLastError($fatal=false,$ifNotFatalGetNormalError=true){
		$result=false;
		if(count($this->errors)>0){
			if($fatal&&$this->isFatalError){
				$result=$this->fatalErrors[count($this->fatalErrors)-1];
			}
			if(!$result&&(($fatal&&$ifNotFatalGetNormalError)||!$fatal)){
				$result=$this->errors[count($this->errors)-1];
			}
		}
		return($result);
	}
	
	/*
	** Data-Logging Routine.
	**  This function opens the log file, inserts the current date, the supplied log data then closes it.
	**  @return bool
	*/
	public function log($data="",$logFile=NULL){
		$result=false;
		$error=false;
		$logFile=($logFile===NULL)?APP_LOG_FILE:$logFile;
		if($this->logger->record($data,$logFile)){
			$result=true;
		}
		return($result);
	}
	
	/*
	** Debugging Routine.
	**
	**  @return void
	*/
	public function debug($title="",$data=false,$log=true,$commentOut=NULL,$stacktrace=false){
		if($this->config->debug==="HIDDEN"){
			$commentOut=true;
		}
		if($stacktrace){
			$trace=$this->stacktrace();
		}
      if(php_sapi_name () == "cli"){
         if($this->config->debug==="ON" || $this->config->debug==="HIDDEN"){
            $output="";
            //$output.=($commentOut===TRUE)?chr(13)."<!--".chr(13):chr(13);
            //$output.='<div id="gravy_debug">'.chr(13);
            $output.="$title :";
            if($data!=false){
               $output.=var_export($data,true);
            }
            if($stacktrace){
               //$output.="	<h2>Stack Trace</h2>".chr(13);
               $output.="$trace".chr(13);
            }

            print($output . "\n");
         }
      }
      else{//web mode
         if($this->config->debug==="ON" || $this->config->debug==="HIDDEN"){
            $output="";
            $output.=($commentOut===TRUE)?chr(13)."<!--".chr(13):chr(13);
            $output.='<div id="gravy_debug">'.chr(13);
            $output.="	<h1>".$title."</h1>".chr(13);
            if($data!=false){
               $output.="	<pre>".chr(13);
               $output.=var_export($data,true);
               $output.="	</pre>".chr(13);
            }
            if($stacktrace){
               $output.="	<h2>Stack Trace</h2>".chr(13);
               $output.="	<pre>$trace</pre>".chr(13);
            }
            $output.="</div>".chr(13);
            $output.=($commentOut===TRUE)?"//-->".chr(13):"";

            print($output);
         }
      }

		if($log){
			$this->log("debug->".$title.": \n".var_export($data,true)."\nStack Trace:{\n".$trace."\n}");
		}
	}
	
	/*
	** Debugging Routine.
	**
	**  @return void
	*/
	static public function trace($title="",$data=false,$stacktrace=true){
		if(DEBUG===TRUE){
			$output="";
			$output.='<div id="gravy_debug">'.chr(13);
			$output.="	<h1>".$title."</h1>".chr(13);
			if($data!=false){
				$output.="	<pre>".chr(13);
				$output.=var_export($data,true);
				$output.="	</pre>".chr(13);
			}
			if($stacktrace){
				$trace=self::stacktrace();
				$output.="<!--".chr(13);
				$output.="	<h2>Stack Trace</h2>".chr(13);
				$output.="	<pre>$trace</pre>".chr(13);
				$output.="//-->".chr(13);
			}
			$output.="</div>".chr(13);

			print($output);
		}
	}
	
	/*
	** Flagging Routine.
	**  This function calls debug, but HTML comments out the output.
	**  @return void
	*/
	public function flag($title="",$data=false){
		if($this->config->debug){
			print("<!--");
		}
		$this->debug($title,$data);
		if($this->config->debug){
			print("//-->");
		}
	}
	
	/*
	** Flagging Routine.
	**  This function calls debug, but HTML comments out the output.
	**  @return void
	*/
	public function debugErrors(){
		$this->debug("isError",$this->isError);
		$this->debug("Errors",count($this->errors));
		for($c=0;$c<count($this->errors);$c++){
			$this->debug("error",$this->errors[$c]);
		}
	}
};
?>
